/** @file    Thread.cpp
 *  @author  Alessandro Polo
 *  @version $Id: Thread.cpp 2839 2010-08-04 21:48:26Z alex $
 *  @brief
 * File containing methods for the wosh::Thread class.
 * The header for this class can be found in Thread.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/Thread.h>
 #include <core/ObjectAllocator.h>
 #include <core/ThreadManager.h>
 #include <core/Object.h>
 #include <core/Utilities.h>
 #include <core/DataModels.h>
// #include <stdio.h> //@bug BUG: TEMP TO BE REMOVED
//	#include <unistd.h>	// void usleep(long);


using namespace std;
namespace wosh {

 WOSH_REGISTER_INTERFACE(wosh::Thread, "", 1.01, _static_Thread )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Thread::Thread( const Object* owner, const std::string& threadName ) {
	this->id = 0;
	this->running = false;
	this->threadname = threadName;
	this->aliveTs = -1;
	this->aliveVariance = 10000;
	this->ownerObj = owner;
	this->threadListener = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void Thread::setThreadID( long thread_id ) {
	this->id = thread_id;
 }

void Thread::setThreadState( Thread::THREAD_STATE state, bool raiseEvent ) {
	this->threadState = state;
	if ( this->threadListener != NULL )
		this->threadListener->thread_event( state, this );
	if ( raiseEvent )
		ThreadManager::push_event( state, this );
 }

void Thread::setThreadAlive() {
	this->aliveTs = Utilities::std_time();
	if ( getThreadState() == Thread::STATE_CRASHED ) {
		// must be a wrong notification (due to timeout)
		// restore the state
		log(LOG_WARNING, "Thread::setThreadAlive()", "Forcing state-switch from CRASHED state [%ld, %s]", getThreadID(), getThreadName().c_str()  );
		if ( isThreadRunningInternal() )
			setThreadState( Thread::STATE_RUNNING, true );
		else
			setThreadState( Thread::STATE_FAILURE, true );
	 }
 }

bool Thread::isThreadRunning() const {
	if ( this->threadState == Thread::STATE_RUNNING )
		return true;
	return false;
 }

bool Thread::isThreadAlive( long millisec_span ) const {
	if ( this->aliveTs == 0 ) return false;
	if ( millisec_span < 0 )
		millisec_span = this->aliveVariance;
	return ( Utilities::std_time() - (long)this->aliveTs < millisec_span/1000 );
 }

bool Thread::waitForMSec( long millisec ) {
	while( this->running && millisec > 0 ) {
		millisec -= 50;
		ThreadImpl::sleepForMSec(50);
	 }
	setThreadAlive();
	return this->running;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS

bool Thread::waitState( const Thread* thread, Thread::THREAD_STATE state, unsigned long maxtime_msec ) {
	if ( thread == NULL ) return false;
	if ( maxtime_msec > 0 )
		maxtime_msec = (unsigned long)(maxtime_msec / 10) + 1;
	unsigned long i = 0;
	while( ++i < maxtime_msec ) {
		if ( thread->getThreadState() == state )
			return true;
		if ( state != Thread::STATE_FAILURE && thread->getThreadState() == Thread::STATE_FAILURE )
			return false;
		ThreadImpl::sleepForMSec(10);
	 }
	return ( thread->getThreadState() == state );
 }

bool Thread::sleepWhile( const Thread* thread, unsigned long maxtime_msec, unsigned long accurency_msec, bool* semaphore0, bool* semaphore1 ) {
	if ( thread == NULL ) return false;
	if ( accurency_msec > maxtime_msec ) accurency_msec = maxtime_msec;
	if ( accurency_msec == 0 ) accurency_msec = maxtime_msec / 100; // 1% accurancy
	maxtime_msec = (unsigned long)(maxtime_msec / accurency_msec) + 1;
	unsigned long i = 0;
	while( ++i < maxtime_msec ) {
		if ( !thread->running ) return false;
		if ( semaphore0 != NULL && !*semaphore0 ) return false;
		if ( semaphore1 != NULL && !*semaphore1 ) return false;
		ThreadImpl::sleepForMSec(accurency_msec);
		thread->aliveTs = Utilities::std_time();
	 }
	return true;
 }

bool Thread::sleepWhile( Thread* thread, unsigned long maxtime_msec, unsigned long accurency_msec, bool* semaphore0, bool* semaphore1 ) {
	if ( thread == NULL ) return false;
	if ( accurency_msec > maxtime_msec ) accurency_msec = maxtime_msec;
	if ( accurency_msec == 0 ) accurency_msec = maxtime_msec / 100; // 1% accurancy
	maxtime_msec = (unsigned long)(maxtime_msec / accurency_msec) + 1;
	unsigned long i = -1;
	while( ++i < maxtime_msec ) {
		if ( !thread->running ) return false;
		if ( semaphore0 != NULL && !*semaphore0 ) return false;
		if ( semaphore1 != NULL && !*semaphore1 ) return false;
		ThreadImpl::sleepForMSec(accurency_msec);
		thread->setThreadAlive();
	 }
	return true;
 }

////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<Thread>* Thread::getDataFields() {
	DataFieldCollector<Thread>* fields = new DataFieldCollector<Thread>();
	fields->add<long, Variant>(&Thread::getThreadID, "ID", 0);
	fields->add<const std::string&, Variant>(&Thread::getThreadName, "Name", 1);
	fields->add<const char*, Variant>(&Thread::getClassName, "Type", 2);
	fields->add<const char*,Variant>(&Thread::getThreadStateAsString, "State", 3);
	fields->add<Thread::THREAD_STATE,Variant>(&Thread::getThreadState, "State[int]", 3, false);
	fields->add<long,Variant_DT>(&Thread::getThreadAliveTs, "Alive", 4);
	fields->add<long,Variant_TS>(&Thread::getThreadAliveVariance, "AliveVariance", 5);
	DataFieldSubWrapperT<Thread, const Object*, const std::string&, Variant>* dfwNameStr;
	dfwNameStr = new DataFieldSubWrapperT<Thread, const Object*, const std::string&, Variant>(&Thread::getOwner, &Object::getName, "OwnerName", 6);
	dfwNameStr->info.visible = true;
	fields->add(dfwNameStr);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* ThreadStateStr[] = {
	"UNKNOWN",		// (THREAD_STATE)0
	"CREATED",		// (THREAD_STATE)1
	"STARTING",		// (THREAD_STATE)2
	"RUNNING",		// (THREAD_STATE)3
	"STOPPING",		// (THREAD_STATE)4
	"STOPPED",		// (THREAD_STATE)5
	"DESTROYED",	// (THREAD_STATE)6
	"CRASHED",		// (THREAD_STATE)7
	"FAILURE"		// (THREAD_STATE)8
};

Enum2StrFromArray(Thread::getThreadStateAsString, Thread::THREAD_STATE, STATE_CREATED, STATE_FAILURE, 0, ThreadStateStr)

Str2EnumByArray(Thread::getThreadStateFromString, Thread::THREAD_STATE, 0, STATE_FAILURE, STATE_UNKNOWN, ThreadStateStr)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined(USE_PTHREAD)
 // see source: core/posix/pthread_thread.cpp
#elif defined(USE_THREAD_WIN32)
 // see source: core/win32/win32_thread.cpp
#else // else/_THREAD_QT
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// DEFAULT (BUILT-IN): QT QThread based implementation:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

ThreadImpl_QThread::ThreadImpl_QThread( const Object* owner, const std::string& threadName )
	: QThread(), Thread(owner, threadName) {
	// changes the thread affinity for this object and its children, is it right!?
	moveToThread(this);
	setThreadState( Thread::STATE_CREATED );
 }

ThreadImpl_QThread::~ThreadImpl_QThread() {
	setThreadState( Thread::STATE_DESTROYED );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT ThreadImpl_QThread::startThread( unsigned long synch_timeout ) {
	QThread::start();
	if ( synch_timeout == 1 ) {
//		printf("\nWRESULT Thread::startThread( 1 ) [%s]\n", getThreadName().c_str() );
	}
	bool started = Thread::waitState( this, Thread::STATE_RUNNING, synch_timeout );
	if ( started )
		return WRET_OK;
	// probably the initializeThread() returned false! or waiting timed out..
	// let' the thread exit RUN call
	// else we may edit/destroy thread while still running
	// this is because initializeThread() is inside RUN()!
	if ( getThreadState() == STATE_FAILURE )
		QThread::wait();
	if ( synch_timeout == 0 ) // was asynch
		return WRET_OK;
	return WRET_ERR_INTERNAL;
 }

WRESULT ThreadImpl_QThread::quitThread( unsigned long synch_timeout ) {
	this->running = false; // stop implemented loop if any
	QThread::quit();
	if ( synch_timeout == 1 ) {
//		printf("\nWRESULT Thread::quitThread( 1 ) [%s]\n", getThreadName().c_str() );
	}
	bool stopped = Thread::waitState( this, Thread::STATE_STOPPED, synch_timeout );
	if ( stopped || synch_timeout == 0 )
		return WRET_OK;
	return WRET_ERR_INTERNAL;
 }

bool ThreadImpl_QThread::waitThread( unsigned long time ) {
	return QThread::wait(time);
}

void ThreadImpl_QThread::terminateThread() {
	QThread::terminate();
 }

bool ThreadImpl_QThread::isThreadRunningInternal() const {
	return QThread::isRunning();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void ThreadImpl_QThread::run() {
	setThreadID( (long)QThread::currentThreadId() );
	setThreadState( Thread::STATE_STARTING, true );
	bool initialized = initializingThread();
	if ( !initialized ) {
		this->running = false;
		setThreadState( Thread::STATE_FAILURE, true );
		return;
	 }
	setThreadState( Thread::STATE_RUNNING, true );
	this->running = true;
	runThread();
	setThreadState( Thread::STATE_STOPPING, true );
	this->running = false;
	exitingThread();
	if ( getThreadState() == Thread::STATE_STOPPING )
		setThreadState( Thread::STATE_STOPPED, true );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
#endif // else/_THREAD_QT  :  DEFAULT (BUILT-IN): QT QThread based implementation
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
